{
  "cells": [
    {
      "metadata": {
        "id": "nUeGCs3TUy8h"
      },
      "cell_type": "markdown",
      "source": [
        "Project: /meridian/_project.yaml\n",
        "Book: /meridian/_book.yaml\n",
        "\n",
        "<style>\n",
        "devsite-code .tfo-notebook-code-cell-output {\n",
        "  max-height: 300px;\n",
        "  overflow: auto;\n",
        "  background: rgba(255, 247, 237, 1);  /* orange bg to distinguish from input code cells */\n",
        "}\n",
        "\n",
        "devsite-code .tfo-notebook-code-cell-output + .devsite-code-buttons-container button {\n",
        "  background: rgba(255, 247, 237, .7);  /* orange bg to distinguish from input code cells */\n",
        "}\n",
        "\n",
        "devsite-code[dark-code] .tfo-notebook-code-cell-output {\n",
        "  background: rgba(64, 78, 103, 1);  /* medium slate */\n",
        "}\n",
        "\n",
        "devsite-code[dark-code] .tfo-notebook-code-cell-output + .devsite-code-buttons-container button {\n",
        "  background: rgba(64, 78, 103, .7);  /* medium slate */\n",
        "}\n",
        "\n",
        "/* override default table styles for notebook buttons */\n",
        ".devsite-table-wrapper .tfo-notebook-buttons {\n",
        "  display: inline-block;\n",
        "  margin-left: 3px;\n",
        "  width: auto;\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons tr {\n",
        "  background: 0;\n",
        "  border: 0;\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons td {\n",
        "  padding-left: 0;\n",
        "  padding-right: 20px;\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons {\n",
        "  --tfo-notebook-buttons-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .3), 0 1px 3px 1px rgba(60, 64, 67, .15);\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons a,\n",
        ".tfo-notebook-buttons :link,\n",
        ".tfo-notebook-buttons :visited {\n",
        "  border-radius: 8px;\n",
        "  box-shadow: var(--tfo-notebook-buttons-box-shadow);\n",
        "  color: #202124;\n",
        "  padding: 12px 24px;\n",
        "  transition: box-shadow 0.2s;\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons a:hover,\n",
        ".tfo-notebook-buttons a:focus {\n",
        "  box-shadow: var(--tfo-notebook-buttons-box-shadow);\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons td > a {\n",
        "  -webkit-box-align: center;\n",
        "  -ms-flex-align: center;\n",
        "  align-items: center;\n",
        "  display: -webkit-box;\n",
        "  display: -ms-flexbox;\n",
        "  display: flex;\n",
        "}\n",
        "\n",
        ".tfo-notebook-buttons td > a > img {\n",
        "  margin-right: 8px;\n",
        "}\n",
        "</style>"
      ]
    },
    {
      "metadata": {
        "id": "yuQtvbG_vILv"
      },
      "cell_type": "markdown",
      "source": [
        "<table class=\"tfo-notebook-buttons tfo-api nocontent\" align=\"left\">\n",
        "  <tbody>\n",
        "    <tr>\n",
        "      <td>\n",
        "        <a target=\"_blank\" href=\"https://colab.research.google.com/github/google/meridian/blob/main/demo/Meridian_Getting_Started.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "      </td>\n",
        "      <td>\n",
        "        <a target=\"_blank\" href=\"https://github.com/google/meridian/blob/main/demo/Meridian_Getting_Started.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "      </td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>"
      ]
    },
    {
      "metadata": {
        "id": "KqSiFABximWU"
      },
      "cell_type": "markdown",
      "source": [
        "# **Introduction to Meridian Demo**"
      ]
    },
    {
      "metadata": {
        "id": "ckR-pavwis-Q"
      },
      "cell_type": "markdown",
      "source": [
        "Welcome to the Meridian end-to-end demo. This simplified demo showcases the fundamental functionalities and basic usage of the library, including working examples of the major modeling steps:\n",
        "\n",
        "\n",
        "<ol start=\"0\">\n",
        "  <li><a href=\"#install\">Install</a></li>\n",
        "  <li><a href=\"#load-data\">Load the data</a></li>\n",
        "  <li><a href=\"#configure-model\">Configure the model</a></li>\n",
        "  <li><a href=\"#model-diagnostics\">Run model diagnostics</a></li>\n",
        "  <li><a href=\"#generate-summary\">Generate model results & two-page output</a></li>\n",
        "  <li><a href=\"#generate-optimize\">Run budget optimization & two-page output</a></li>\n",
        "  <li><a href=\"#save-model\">Save the model object</a></li>\n",
        "</ol>\n",
        "\n",
        "\n",
        "Note that this notebook skips all of the exploratory data analysis and preprocessing steps. It assumes that you have completed these tasks before reaching this point in the demo.\n",
        "\n",
        "This notebook utilizes sample data. As a result, the numbers and results obtained might not accurately reflect what you encounter when working with a real dataset."
      ]
    },
    {
      "metadata": {
        "id": "GicRPam0mUhF"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"install\"></a>\n",
        "## Step 0: Install"
      ]
    },
    {
      "metadata": {
        "id": "pDdX9WofM2fx"
      },
      "cell_type": "markdown",
      "source": [
        "1\\. Make sure you are using one of the available GPU Colab runtimes which is **required** to run Meridian. You can change your notebook's runtime in `Runtime > Change runtime type` in the menu. All users can use the T4 GPU runtime which is sufficient to run the demo colab, free of charge. Users who have purchased one of Colab's paid plans have access to premium GPUs (such as V100, A100 or L4 Nvidia GPU)."
      ]
    },
    {
      "metadata": {
        "id": "nFYRTDuesa1P"
      },
      "cell_type": "markdown",
      "source": [
        "2\\. Install the latest version of Meridian, and verify that GPU is available."
      ]
    },
    {
      "metadata": {
        "id": "h1jAk386jF3k"
      },
      "cell_type": "code",
      "source": [
        "# Install meridian: from PyPI @ latest release\n",
        "!pip install --upgrade google-meridian[colab,and-cuda,schema]\n",
        "\n",
        "# Install meridian: from PyPI @ specific version\n",
        "# !pip install google-meridian[colab,and-cuda,schema]==1.3.1\n",
        "\n",
        "# Install meridian: from GitHub @HEAD\n",
        "# !pip install --upgrade \"google-meridian[colab,and-cuda,schema] @ git+https://github.com/google/meridian.git@main\""
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "Fhwt1wzgLwpZ"
      },
      "cell_type": "code",
      "source": [
        "import IPython\n",
        "from meridian import constants\n",
        "from meridian.analysis import analyzer\n",
        "from meridian.analysis import optimizer\n",
        "from meridian.analysis import summarizer\n",
        "from meridian.analysis import visualizer\n",
        "from meridian.analysis.review import reviewer\n",
        "from meridian.data import data_frame_input_data_builder\n",
        "from meridian.model import model\n",
        "from meridian.model import prior_distribution\n",
        "from meridian.model import spec\n",
        "from schema.serde import meridian_serde\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "# check if GPU is available\n",
        "from psutil import virtual_memory\n",
        "import tensorflow as tf\n",
        "import tensorflow_probability as tfp\n",
        "\n",
        "ram_gb = virtual_memory().total / 1e9\n",
        "print('Your runtime has {:.1f} gigabytes of available RAM\\n'.format(ram_gb))\n",
        "print(\n",
        "    'Num GPUs Available: ',\n",
        "    len(tf.config.experimental.list_physical_devices('GPU')),\n",
        ")\n",
        "print(\n",
        "    'Num CPUs Available: ',\n",
        "    len(tf.config.experimental.list_physical_devices('CPU')),\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "kiM0UrN6qbIP"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"load-data\"></a>\n",
        "## Step 1: Load the data"
      ]
    },
    {
      "metadata": {
        "id": "z18Mo-22x0lY"
      },
      "cell_type": "markdown",
      "source": [
        "Load the [simulated dataset in CSV format](https://github.com/google/meridian/blob/main/meridian/data/simulated_data/csv/geo_all_channels.csv) as follows."
      ]
    },
    {
      "metadata": {
        "id": "tZd-ik8NbjK6"
      },
      "cell_type": "markdown",
      "source": [
        "1\\. Read the data into a Pandas DataFrame."
      ]
    },
    {
      "metadata": {
        "id": "7sV1ChiEYuyD"
      },
      "cell_type": "code",
      "source": [
        "df = pd.read_csv(\n",
        "    \"https://raw.githubusercontent.com/google/meridian/refs/heads/main/meridian/data/simulated_data/csv/geo_all_channels.csv\"\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "8JBDZzl80BrY"
      },
      "cell_type": "markdown",
      "source": [
        "2\\. Create a DataFrameInputDataBuilder instance."
      ]
    },
    {
      "metadata": {
        "id": "4qdTSk4a0znn"
      },
      "cell_type": "code",
      "source": [
        "builder = data_frame_input_data_builder.DataFrameInputDataBuilder(\n",
        "    kpi_type='non_revenue',\n",
        "    default_kpi_column='conversions',\n",
        "    default_revenue_per_kpi_column='revenue_per_conversion',\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "LNr75vQL1Zru"
      },
      "cell_type": "markdown",
      "source": [
        "3\\. Offer the components to the builder. Note that the components may be offered all at once or piecewise."
      ]
    },
    {
      "metadata": {
        "id": "udaLGvwl1U8B"
      },
      "cell_type": "code",
      "source": [
        "builder = (\n",
        "    builder.with_kpi(df)\n",
        "    .with_revenue_per_kpi(df)\n",
        "    .with_population(df)\n",
        "    .with_controls(\n",
        "        df, control_cols=[\"sentiment_score_control\", \"competitor_sales_control\"]\n",
        "    )\n",
        ")\n",
        "\n",
        "channels = [\"Channel0\", \"Channel1\", \"Channel2\", \"Channel3\", \"Channel4\"]\n",
        "builder = builder.with_media(\n",
        "    df,\n",
        "    media_cols=[f\"{channel}_impression\" for channel in channels],\n",
        "    media_spend_cols=[f\"{channel}_spend\" for channel in channels],\n",
        "    media_channels=channels,\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "ax0lKJwIc81Z"
      },
      "cell_type": "markdown",
      "source": [
        "4. If your data includes organic media or non-media treatments, you can add them using `with_organic_media` and `with_non_media_treatments` methods. For the definition of each variable, see\n",
        "[Collect and organize your data](https://developers.google.com/meridian/docs/pre-modeling/collect-data)"
      ]
    },
    {
      "metadata": {
        "id": "GXmbyOQ8c88c"
      },
      "cell_type": "code",
      "source": [
        "builder = builder.with_non_media_treatments(\n",
        "    df, non_media_treatment_cols=['Promo']\n",
        ").with_organic_media(\n",
        "    df,\n",
        "    organic_media_cols=['Organic_channel0_impression'],\n",
        "    organic_media_channels=['Organic_channel0'],\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "fquwpjiMc9DB"
      },
      "cell_type": "markdown",
      "source": [
        "5. Finally, build the InputData."
      ]
    },
    {
      "metadata": {
        "id": "5JYSFLRMc9In"
      },
      "cell_type": "code",
      "source": [
        "data = builder.build()"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "DlF5vs8vb8Wn"
      },
      "cell_type": "markdown",
      "source": [
        "Note that the simulated data here does not contain reach and frequency. We recommend including reach and frequency data whenever they are available. For information about the advantages of utilizing reach and frequency, see [Bayesian Hierarchical Media Mix Model Incorporating Reach and Frequency Data](https://research.google/pubs/bayesian-hierarchical-media-mix-model-incorporating-reach-and-frequency-data/#:~:text=By%20incorporating%20R%26F%20into%20MMM,based%20on%20optimal%20frequency%20recommendations.). For code snippet for loading reach and frequency data, see [Load geo-level data with reach and frequency](https://developers.google.com/meridian/docs/user-guide/load-geo-data-with-rf)\n",
        "\n",
        "The documentation provides guidance for instances where reach and frequency data is accessible for specific channels. Additionally, for information about how to load other data types and formats, including data with reach and frequency, see [Supported data types and formats](https://developers.google.com/meridian/docs/user-guide/supported-data-types-formats)."
      ]
    },
    {
      "metadata": {
        "id": "FO6pDd6f2V1L"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"configure-model\"></a>\n",
        "## Step 2: Configure the model"
      ]
    },
    {
      "metadata": {
        "id": "a_mQI7HzxxK4"
      },
      "cell_type": "markdown",
      "source": [
        "Meridian uses Bayesian framework and Markov Chain Monte Carlo (MCMC) algorithms to sample from the posterior distribution.\n",
        "\n",
        "1\\. Inititalize the `Meridian` class by passing the loaded data and the customized model specification. One advantage of Meridian lies in its capacity to calibrate the model directly through ROI priors, as described in [Media Mix Model Calibration With Bayesian Priors](https://research.google/pubs/media-mix-model-calibration-with-bayesian-priors/). In this particular example, the ROI priors for all media channels are identical, with each being represented as Lognormal(0.2, 0.9)."
      ]
    },
    {
      "metadata": {
        "id": "8XNDd7HX1qTn"
      },
      "cell_type": "code",
      "source": [
        "roi_mu = 0.2  # Mu for ROI prior for each media channel.\n",
        "roi_sigma = 0.9  # Sigma for ROI prior for each media channel.\n",
        "prior = prior_distribution.PriorDistribution(\n",
        "    roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, name=constants.ROI_M)\n",
        ")\n",
        "model_spec = spec.ModelSpec(prior=prior, enable_aks=True)\n",
        "\n",
        "mmm = model.Meridian(input_data=data, model_spec=model_spec)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "kPQBPlX8cmEv"
      },
      "cell_type": "markdown",
      "source": [
        "2\\. Use the `sample_prior()` and `sample_posterior()` methods to obtain samples from the prior and posterior distributions of model parameters. If you are using the T4 GPU runtime this step may take about 10 minutes for the provided data set."
      ]
    },
    {
      "metadata": {
        "id": "KVB3avRdcRNz"
      },
      "cell_type": "code",
      "source": [
        "%%time\n",
        "mmm.sample_prior(500)\n",
        "mmm.sample_posterior(\n",
        "    n_chains=10, n_adapt=2000, n_burnin=500, n_keep=1000, seed=0\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "5WUM2V26cspo"
      },
      "cell_type": "markdown",
      "source": [
        "For more information about configuring the parameters and using a customized model specification, such as setting different ROI priors for each media channel, see [Configure the model](https://developers.google.com/meridian/docs/user-guide/configure-model)."
      ]
    },
    {
      "metadata": {
        "id": "mV7LkUe9oZhO"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"quality-checks\"></a>\n",
        "## Step 3: Run post-modeling quality checks"
      ]
    },
    {
      "metadata": {
        "id": "HD_13gkAobKu"
      },
      "cell_type": "markdown",
      "source": [
        "These post-modeling quality checks are designed to diagnose common issues related to model convergence, specification, and plausibility. Run the following command to generate the results for all necessary diagnostics:"
      ]
    },
    {
      "metadata": {
        "id": "IOM9p7jsoeKo"
      },
      "cell_type": "code",
      "source": [
        "reviewer.ModelReviewer(mmm).run()"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "t9oECJwUdJTm"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"model-diagnostics\"></a>\n",
        "## Step 4: Run model diagnostics"
      ]
    },
    {
      "metadata": {
        "id": "kSzK6JeMxrV6"
      },
      "cell_type": "markdown",
      "source": [
        "To further assess convergence and model fit, you can use the methods from `visualizer` module.\n",
        "\n",
        "1\\. Assess convergence. Run the following code to generate r-hat statistics. R-hat close to 1.0 indicate convergence. R-hat < 1.2 indicates approximate convergence and is a reasonable threshold for many problems."
      ]
    },
    {
      "metadata": {
        "id": "rFuc7B86yLvM"
      },
      "cell_type": "code",
      "source": [
        "model_diagnostics = visualizer.ModelDiagnostics(mmm)\n",
        "model_diagnostics.plot_rhat_boxplot()"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "nCwt5SGYxlaE"
      },
      "cell_type": "markdown",
      "source": [
        "2\\. Assess the model's fit by comparing the expected sales against the actual sales."
      ]
    },
    {
      "metadata": {
        "id": "7Z4zJtHyyhif"
      },
      "cell_type": "code",
      "source": [
        "model_fit = visualizer.ModelFit(mmm)\n",
        "model_fit.plot_model_fit()"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "76IBQcWLu980"
      },
      "cell_type": "markdown",
      "source": [
        "For more information and additional model diagnostics checks, see [Modeling diagnostics](https://developers.google.com/meridian/docs/user-guide/model-diagnostics)."
      ]
    },
    {
      "metadata": {
        "id": "zGUOFFbCdOtl"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"generate-summary\"></a>\n",
        "## Step 5: Generate model results & two-page output"
      ]
    },
    {
      "metadata": {
        "id": "puHjkyvZEOEg"
      },
      "cell_type": "markdown",
      "source": [
        "To export the two-page HTML summary output, initialize the `Summarizer` class with the model object. Then pass in the filename, filepath, start date, and end date to `output_model_results_summary` to run the summary for that time duration and save it to the specified file."
      ]
    },
    {
      "metadata": {
        "id": "keOpq1qKNbq0"
      },
      "cell_type": "code",
      "source": [
        "mmm_summarizer = summarizer.Summarizer(mmm)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "Ltr4uP80YQe7"
      },
      "cell_type": "code",
      "source": [
        "from google.colab import drive\n",
        "\n",
        "drive.mount('/content/drive')"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "qbgNaDYpIfQl"
      },
      "cell_type": "code",
      "source": [
        "filepath = '/content/drive/MyDrive'\n",
        "start_date = '2021-01-25'\n",
        "end_date = '2024-01-15'\n",
        "mmm_summarizer.output_model_results_summary(\n",
        "    'summary_output.html', filepath, start_date, end_date\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "j9sBxuvidmr8"
      },
      "cell_type": "markdown",
      "source": [
        "Here is a preview of the two-page output based on the simulated data:"
      ]
    },
    {
      "metadata": {
        "id": "vaUe7uZRfJPm"
      },
      "cell_type": "code",
      "source": [
        "IPython.display.HTML(filename='/content/drive/MyDrive/summary_output.html')"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "PphWMfKdwPIw"
      },
      "cell_type": "markdown",
      "source": [
        "For a customized two-page report, model results summary table, and individual visualizations, see [Model results report](https://developers.google.com/meridian/docs/user-guide/generate-model-results-report) and [plot media visualizations](https://developers.google.com/meridian/docs/user-guide/plot-media-visualizations).\n",
        "\n",
        "\n",
        "\n"
      ]
    },
    {
      "metadata": {
        "id": "msqwz2MN5mTq"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"generate-optimize\"></a>\n",
        "## Step 6: Run budget optimization & generate an optimization report"
      ]
    },
    {
      "metadata": {
        "id": "khCL6Q2sS-iy"
      },
      "cell_type": "markdown",
      "source": [
        "You can choose what scenario to run for the budget allocation. In default scenario, you find the optimal allocation across channels for a given budget to maximize the return on investment (ROI).\n",
        "\n",
        "1\\. Instantiate the `BudgetOptimizer` class and run the `optimize()` method without any customization, to run the default library's Fixed Budget Scenario to maximize ROI."
      ]
    },
    {
      "metadata": {
        "id": "38lhqyLvHf51"
      },
      "cell_type": "code",
      "source": [
        "%%time\n",
        "budget_optimizer = optimizer.BudgetOptimizer(mmm)\n",
        "optimization_results = budget_optimizer.optimize()"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "fLOMqDmCRKRO"
      },
      "cell_type": "markdown",
      "source": [
        "2\\. Export the 2-page HTML optimization report, which contains optimized spend allocations and ROI."
      ]
    },
    {
      "metadata": {
        "id": "at7V7YEh_zwZ"
      },
      "cell_type": "code",
      "source": [
        "filepath = '/content/drive/MyDrive'\n",
        "optimization_results.output_optimization_summary(\n",
        "    'optimization_output.html', filepath\n",
        ")"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "jq_mcrj1STDU"
      },
      "cell_type": "code",
      "source": [
        "IPython.display.HTML(filename='/content/drive/MyDrive/optimization_output.html')"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "kIWTubaN0RKC"
      },
      "cell_type": "markdown",
      "source": [
        "For information about customized optimization scenarios, such as flexible budget scenarios, see [Budget optimization scenarios](https://developers.google.com/meridian/docs/user-guide/budget-optimization-scenarios). For more information about optimization results summary and individual visualizations, see [optimization results output](https://developers.google.com/meridian/docs/user-guide/generate-optimization-results-output) and [optimization visualizations](https://developers.google.com/meridian/docs/user-guide/plot-optimization-visualizations).\n",
        "\n",
        "\n",
        "Optimization can also be performed on a hypothetical data representing a future scenario. The new data takes the same structure as the input data and encodes an anticipated flighting pattern, cost per media unit, and revenue per kpi.\n",
        "\n",
        "3\\. Load the [simulated dataset in CSV format](https://github.com/google/meridian/blob/main/meridian/data/simulated_data/csv/hypothetical_geo_all_channels.csv) into Pandas DataFrame."
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "df = pd.read_csv(\n",
        "    \"https://raw.githubusercontent.com/google/meridian/refs/heads/main/meridian/data/simulated_data/csv/hypothetical_geo_all_channels.csv\"\n",
        ")\n"
      ],
      "metadata": {
        "id": "8vqjV3teqXLh"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "4\\. New data is read from a csv file and converted into a set of multi-dimensional arrays. The arrays are used to construct a `DataTensors` instance, which is passed to `optimize()` as the `new_data` argument.\n",
        "\n",
        "Constructing a `DataTensors` instance requires that all arrays have \"time\" and \"geo\" dimensions. Alternatively, the `BudgetOptimizer.create_optimization_tensors` method can be used to construct a `DataTensors` instance. This helper method can simplify the process, particularly when you do not need \"time\" and \"geo\" dimensions for all inputs. For example, it can be convenient if you want to assume a constant \"revenue per kpi\" or \"cost per media unit\" for all geos and time periods."
      ],
      "metadata": {
        "id": "1p3CZ-p8qalg"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "n_geos = mmm.n_geos\n",
        "n_media_channels = mmm.n_media_channels\n",
        "n_non_media_channels = mmm.n_non_media_channels\n",
        "n_organic_media_channels = mmm.n_organic_media_channels\n",
        "\n",
        "# The number of time periods and time range do not need to match the input data.\n",
        "df[constants.TIME] = pd.to_datetime(df[constants.TIME], errors='coerce')\n",
        "unique_times = sorted(df[constants.TIME].unique())\n",
        "n_times = len(unique_times)\n",
        "\n",
        "geos = mmm.input_data.geo.values\n",
        "media_channels = mmm.input_data.media_channel.values\n",
        "media_cols = [f\"{channel}_impression\" for channel in media_channels]\n",
        "media_spend_cols = [f\"{channel}_spend\" for channel in media_channels]\n",
        "non_media_treatment_cols = ['Promo']\n",
        "organic_media_cols = ['Organic_channel0_impression']\n",
        "organic_media_channels = ['Organic_channel0']\n",
        "revenue_per_kpi_col='revenue_per_conversion'\n",
        "times_str = [time.strftime(constants.DATE_FORMAT) for time in unique_times]\n",
        "\n",
        "media_np = np.zeros((n_geos, n_times, n_media_channels))\n",
        "media_spend_np = np.zeros((n_geos, n_times, n_media_channels))\n",
        "non_media_treatment_np = np.zeros((n_geos, n_times, n_non_media_channels))\n",
        "organic_media_np = np.zeros((n_geos, n_times, n_organic_media_channels))\n",
        "revenue_per_kpi_np = np.zeros((n_geos, n_times))\n",
        "\n",
        "df_grouped = df.set_index([constants.GEO, constants.TIME])\n",
        "for geo_idx, geo in enumerate(geos):\n",
        "  for time_idx, time in enumerate(unique_times):\n",
        "    row = df_grouped.loc[(geo, time)]\n",
        "    media_np[geo_idx, time_idx, :] = row[media_cols].values\n",
        "    media_spend_np[geo_idx, time_idx, :] = row[media_spend_cols].values\n",
        "    non_media_treatment_np[geo_idx, time_idx, :] = row[non_media_treatment_cols].values\n",
        "    organic_media_np[geo_idx, time_idx, :] = row[organic_media_cols].values\n",
        "    revenue_per_kpi_np[geo_idx, time_idx] = row[revenue_per_kpi_col].item()\n",
        "\n",
        "data_tensors = analyzer.DataTensors(\n",
        "    media=tf.convert_to_tensor(media_np, dtype=tf.float32),\n",
        "    media_spend=tf.convert_to_tensor(media_spend_np, dtype=tf.float32),\n",
        "    non_media_treatments=tf.convert_to_tensor(non_media_treatment_np, dtype=tf.float32),\n",
        "    organic_media=tf.convert_to_tensor(organic_media_np, dtype=tf.float32),\n",
        "    revenue_per_kpi=tf.convert_to_tensor(revenue_per_kpi_np, dtype=tf.float32),\n",
        "    time=tf.convert_to_tensor(times_str, dtype=tf.string),\n",
        ")\n",
        "# Default values for `budget` and `pct_of_spend` are derived from the `new_data`,\n",
        "# but these values can be overridden without modifying the `new_data` itself.\n",
        "hypothetical_optimization_results = budget_optimizer.optimize(\n",
        "    new_data=data_tensors,\n",
        "    budget=50_000_000,\n",
        "    pct_of_spend=[.2, .1, .2, .2, .3]\n",
        ")"
      ],
      "metadata": {
        "id": "GD6m-NlTqlP6"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "5\\. Export the 2-page HTML optimization report."
      ],
      "metadata": {
        "id": "3TmXPtpSqrkV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "hypothetical_optimization_results.output_optimization_summary(\n",
        "    'hypothetical_optimization_output.html', filepath\n",
        ")"
      ],
      "metadata": {
        "id": "6e5u4uK4qsVg"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "IPython.display.HTML(filename='/content/drive/MyDrive/hypothetical_optimization_output.html')"
      ],
      "metadata": {
        "id": "4wB3w9nTq1Y6"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "metadata": {
        "id": "3m98O3a_TrVg"
      },
      "cell_type": "markdown",
      "source": [
        "<a name=\"save-model\"></a>\n",
        "## Step 7: Save the model object"
      ]
    },
    {
      "metadata": {
        "id": "2Zjh64YG8Dti"
      },
      "cell_type": "markdown",
      "source": [
        "We recommend that you save the model object for future use. This helps you to  avoid repetitive model runs and saves time and computational resources. After the model object is saved, you can load it at a later stage to continue the analysis or visualizations without having to re-run the model.\n"
      ]
    },
    {
      "metadata": {
        "id": "1kamZpyv8KMh"
      },
      "cell_type": "markdown",
      "source": [
        "Run the following codes to save the model object:"
      ]
    },
    {
      "metadata": {
        "id": "FfaQQ8-fTw0K"
      },
      "cell_type": "code",
      "source": [
        "file_path = '/content/drive/MyDrive/saved_mmm.binpb'\n",
        "meridian_serde.save_meridian(mmm, file_path)"
      ],
      "outputs": [],
      "execution_count": null
    },
    {
      "metadata": {
        "id": "k2v_s2uS8PgA"
      },
      "cell_type": "markdown",
      "source": [
        "Run the following codes to load the saved model:"
      ]
    },
    {
      "metadata": {
        "id": "ZGUmiYI48epA"
      },
      "cell_type": "code",
      "source": [
        "mmm = meridian_serde.load_meridian(file_path)"
      ],
      "outputs": [],
      "execution_count": null
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "gpuType": "T4",
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
